
/*-------------------------------------------------------------------------
 *
 * parse_target.c
 *	  处理目标列表
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/parser/parse_target.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "catalog/pg_type.h"
#include "commands/dbcommands.h"
#include "funcapi.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_coerce.h"
#include "parser/parse_expr.h"
#include "parser/parse_func.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parse_type.h"
#include "parser/parsetree.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/typcache.h"

static void fc_markTargetListOrigin(ParseState *fc_pstate, TargetEntry *fc_tle,
								 Var *fc_var, int fc_levelsup);
static Node *fc_transformAssignmentSubscripts(ParseState *fc_pstate,
										   Node *fc_basenode,
										   const char *fc_targetName,
										   Oid fc_targetTypeId,
										   int32 fc_targetTypMod,
										   Oid fc_targetCollation,
										   List *fc_subscripts,
										   bool fc_isSlice,
										   List *fc_indirection,
										   ListCell *fc_next_indirection,
										   Node *fc_rhs,
										   CoercionContext fc_ccontext,
										   int fc_location);
static List *fc_ExpandColumnRefStar(ParseState *fc_pstate, ColumnRef *fc_cref,
								 bool fc_make_target_entry);
static List *fc_ExpandAllTables(ParseState *fc_pstate, int fc_location);
static List *fc_ExpandIndirectionStar(ParseState *fc_pstate, A_Indirection *fc_ind,
								   bool fc_make_target_entry, ParseExprKind fc_exprKind);
static List *fc_ExpandSingleTable(ParseState *fc_pstate, ParseNamespaceItem *fc_nsitem,
							   int fc_sublevels_up, int fc_location,
							   bool fc_make_target_entry);
static List *fc_ExpandRowReference(ParseState *fc_pstate, Node *fc_expr,
								bool fc_make_target_entry);
static int	fc_FigureColnameInternal(Node *fc_node, char **fc_name);


/*
 * transformTargetEntry()
 * 将任何普通的“表达式类型”节点转换为 targetlist 项。
 * 这被导出，以便 parse_clause.c 能够为不在 targetlist 中的 ORDER/GROUP BY 项生成 targetlist 项。
 *
 * node	（未转换的）值表达式的解析树。
 * expr	转换后的表达式，如果调用者尚未执行，则为 NULL。
 * exprKind 表达式类型（EXPR_KIND_SELECT_TARGET 等）
 * colname 要分配的列名，如果尚未设置，则为 NULL。
 * resjunk 如果目标应标记为 resjunk，即它不想要在最终的投影元组中。
 */
TargetEntry * transformTargetEntry(ParseState *fc_pstate,
					 Node *fc_node,
					 Node *fc_expr,
					 ParseExprKind fc_exprKind,
					 char *fc_colname,
					 bool fc_resjunk)
{
	/* 如果调用者尚未完成，则转换该节点 */
	if (fc_expr == NULL)
	{
		/*
		 * 如果这是一个 SetToDefault 节点，并且我们应该允许它，则直接通过而不修改它。 （如果我们不允许它，transformExpr 将抛出适当的错误。）
		 */
		if (fc_exprKind == EXPR_KIND_UPDATE_SOURCE && IsA(fc_node, SetToDefault))
			fc_expr = fc_node;
		else
			fc_expr = transformExpr(fc_pstate, fc_node, fc_exprKind);
	}

	if (fc_colname == NULL && !fc_resjunk)
	{
		/*
		 * 为没有任何显式 'AS ColumnName' 子句的列生成合适的列名。
		 */
		fc_colname = FigureColname(fc_node);
	}

	return makeTargetEntry((Expr *) fc_expr,
						   (AttrNumber) fc_pstate->p_next_resno++,
						   fc_colname,
						   fc_resjunk);
}


/*
 * transformTargetList()
 * 将 ResTarget 的列表转换为 TargetEntry 的列表。
 *
 * 这段代码在 SELECT、UPDATE 或 RETURNING 列表中表现大致相同；主要是转换给定的表达式（“val”字段）。
 * exprKind 参数在必要时区分这些情况。
 */
List * transformTargetList(ParseState *fc_pstate, List *fc_targetlist,
					ParseExprKind fc_exprKind)
{
	List	   *fc_p_target = NIL;
	bool		fc_expand_star;
	ListCell   *fc_o_target;

	/* 开始时不应有任何剩余的 multiassign 项 */
	Assert(fc_pstate->p_multiassign_exprs == NIL);

	/* 在 SELECT 和 RETURNING 中展开 “something.*”，而不是 UPDATE */
	fc_expand_star = (fc_exprKind != EXPR_KIND_UPDATE_SOURCE);

	foreach(fc_o_target, fc_targetlist)
	{
		ResTarget  *fc_res = (ResTarget *) lfirst(fc_o_target);

		/*
		 * 检查 "something.*"。根据 "something" 的复杂性，星号可能出现在 ColumnRef 的最后一个字段，或作为 A_Indirection 中的最后一个间接项。
		 */
		if (fc_expand_star)
		{
			if (IsA(fc_res->val, ColumnRef))
			{
				ColumnRef  *fc_cref = (ColumnRef *) fc_res->val;

				if (IsA(llast(fc_cref->fields), A_Star))
				{
					/* 它是 something.*，展开为多个项 */
					fc_p_target = list_concat(fc_p_target,
										   fc_ExpandColumnRefStar(fc_pstate,
															   fc_cref,
															   true));
					continue;
				}
			}
			else if (IsA(fc_res->val, A_Indirection))
			{
				A_Indirection *fc_ind = (A_Indirection *) fc_res->val;

				if (IsA(llast(fc_ind->indirection), A_Star))
				{
					/* 它是 something.*，展开为多个项 */
					fc_p_target = list_concat(fc_p_target,
										   fc_ExpandIndirectionStar(fc_pstate,
																 fc_ind,
																 true,
																 fc_exprKind));
					continue;
				}
			}
		}

		/*
		 * 不是 "something.*"，或者我们想将其视为一个普通的整行变量，所以作为单个表达式进行转换
		 */
		fc_p_target = lappend(fc_p_target,
						   transformTargetEntry(fc_pstate,
												fc_res->val,
												NULL,
												fc_exprKind,
												fc_res->name,
												false));
	}

	/*
	 * 如果创建了任何 multiassign resjunk 项，将它们附加到 targetlist 的末尾。 这应该只在 UPDATE 目标列表中发生。 我们不需要担心这些项的编号；transformUpdateStmt 将设置它们的 resnos。
	 */
	if (fc_pstate->p_multiassign_exprs)
	{
		Assert(fc_exprKind == EXPR_KIND_UPDATE_SOURCE);
		fc_p_target = list_concat(fc_p_target, fc_pstate->p_multiassign_exprs);
		fc_pstate->p_multiassign_exprs = NIL;
	}

	return fc_p_target;
}



/***************************************
 * transformExpressionList()
 *
 * 这个函数与 transformTargetList 相同，除了输入列表元素是纯表达式，没有 ResTarget 装饰，输出元素同样只是表达式，没有 TargetEntry 装饰。 另外，我们不期望列表中有任何多重赋值结构，因此对此没有任何操作。我们使用它来处理 ROW() 和 VALUES() 结构。
 *
 * exprKind 不足以告诉我们是否允许 SetToDefault，因此需要一个额外的标志来处理。
 */
List * transformExpressionList(ParseState *fc_pstate, List *fc_exprlist,
						ParseExprKind fc_exprKind, bool fc_allowDefault)
{
	List	   *fc_result = NIL;
	ListCell   *fc_lc;

	foreach(fc_lc, fc_exprlist)
	{
		Node	   *fc_e = (Node *) lfirst(fc_lc);

		/*
		 * 检查 "something.*"。根据 "something" 的复杂性，星号可能出现在 ColumnRef 的最后一个字段，或作为 A_Indirection 中的最后一个间接项。
		 */
		if (IsA(fc_e, ColumnRef))
		{
			ColumnRef  *fc_cref = (ColumnRef *) fc_e;

			if (IsA(llast(fc_cref->fields), A_Star))
			{
				/* 它是 something.*，展开为多个项 */
				fc_result = list_concat(fc_result,
									 fc_ExpandColumnRefStar(fc_pstate, fc_cref,
														 false));
				continue;
			}
		}
		else if (IsA(fc_e, A_Indirection))
		{
			A_Indirection *fc_ind = (A_Indirection *) fc_e;

			if (IsA(llast(fc_ind->indirection), A_Star))
			{
				/* 它是 something.*，展开为多个项 */
				fc_result = list_concat(fc_result,
									 fc_ExpandIndirectionStar(fc_pstate, fc_ind,
														   false, fc_exprKind));
				continue;
			}
		}

		/*
		 * 不是 "something.*"，因此作为一个单独的表达式进行转换。 如果它是一个 SetToDefault 节点并且我们应该允许它，则保持原样传递。（如果我们不允许，transformExpr 将抛出适当的错误。）
		 */
		if (fc_allowDefault && IsA(fc_e, SetToDefault))
			 /* 不执行任何操作 */ ;
		else
			fc_e = transformExpr(fc_pstate, fc_e, fc_exprKind);

		fc_result = lappend(fc_result, fc_e);
	}

	return fc_result;
}


/*
 * resolveTargetListUnknowns()
 *		将任何未知类型的目标列表条目转换为类型 TEXT。
 *
 * 在我们用尽所有其他方法来识别查询的输出列类型后执行此操作。
 */
void resolveTargetListUnknowns(ParseState *fc_pstate, List *fc_targetlist)
{
	ListCell   *fc_l;

	foreach(fc_l, fc_targetlist)
	{
		TargetEntry *fc_tle = (TargetEntry *) lfirst(fc_l);
		Oid			fc_restype = exprType((Node *) fc_tle->expr);

		if (fc_restype == UNKNOWNOID)
		{
			fc_tle->expr = (Expr *) coerce_type(fc_pstate, (Node *) fc_tle->expr,
											 fc_restype, TEXTOID, -1,
											 COERCION_IMPLICIT,
											 COERCE_IMPLICIT_CAST,
											 -1);
		}
	}
}


/*
 * markTargetListOrigins()
 *		标记作为简单 Vars 的目标列表列，带有源表的 OID 和列号。
 *
 * 目前，这仅适用于 SELECT 目标列表和 RETURNING 列表，因为只有在我们要将其发送到前端时才需要这些信息。
 */
void markTargetListOrigins(ParseState *fc_pstate, List *fc_targetlist)
{
	ListCell   *fc_l;

	foreach(fc_l, fc_targetlist)
	{
		TargetEntry *fc_tle = (TargetEntry *) lfirst(fc_l);

		fc_markTargetListOrigin(fc_pstate, fc_tle, (Var *) fc_tle->expr, 0);
	}
}

/*
 * markTargetListOrigin()
 *		如果 'var' 是一个普通关系的 Var，则用其来源标记 'tle'
 *
 * levelsup 是一个额外的偏移量，用于正确解释 Var 的 varlevelsup。
 *
 * 请注意，我们不会深入查看视图，而是将视图报告为列的所有者。也没有必要深入查看联接：如果我们看到一个联接别名 Var，它必须是合并的 JOIN USING 列（或可能是一个整行 Var）；这不是对任何普通表列的直接引用，因此我们不报告它。
 */
static void fc_markTargetListOrigin(ParseState *fc_pstate, TargetEntry *fc_tle,
					 Var *fc_var, int fc_levelsup)
{
	int			fc_netlevelsup;
	RangeTblEntry *fc_rte;
	AttrNumber	fc_attnum;

	if (fc_var == NULL || !IsA(fc_var, Var))
		return;
	fc_netlevelsup = fc_var->varlevelsup + fc_levelsup;
	fc_rte = GetRTEByRangeTablePosn(fc_pstate, fc_var->varno, fc_netlevelsup);
	fc_attnum = fc_var->varattno;

	switch (fc_rte->rtekind)
	{
		case RTE_RELATION:
			/* 这是一个表或视图，报告它 */
			fc_tle->resorigtbl = fc_rte->relid;
			fc_tle->resorigcol = fc_attnum;
			break;
		case RTE_SUBQUERY:
			/* 子选择中的FROM：从子选择复制上来 */
			if (fc_attnum != InvalidAttrNumber)
			{
				TargetEntry *fc_ste = get_tle_by_resno(fc_rte->subquery->targetList,
													fc_attnum);

				if (fc_ste == NULL || fc_ste->resjunk)
					elog(ERROR, "subquery %s does not have attribute %d",
						 fc_rte->eref->aliasname, fc_attnum);
				fc_tle->resorigtbl = fc_ste->resorigtbl;
				fc_tle->resorigcol = fc_ste->resorigcol;
			}
			break;
		case RTE_JOIN:
		case RTE_FUNCTION:
		case RTE_VALUES:
		case RTE_TABLEFUNC:
		case RTE_NAMEDTUPLESTORE:
		case RTE_RESULT:
			/* 不是简单关系，保持未标记 */
			break;
		case RTE_CTE:

			/*
			 * CTE引用：如果可能，从子查询中复制上来。如果
			 * RTE是递归自引用，那么我们无法做任何事情，
			 * 因为我们还没有完成分析。然而，这并不算什么，
			 * 因为我们必须在递归CTE的递归项内部，
			 * 所以当前目标列表上的任何标记都不会影响结果。
			 */
			if (fc_attnum != InvalidAttrNumber && !fc_rte->self_reference)
			{
				CommonTableExpr *fc_cte = GetCTEForRTE(fc_pstate, fc_rte, fc_netlevelsup);
				TargetEntry *fc_ste;
				List	   *fc_tl = GetCTETargetList(fc_cte);
				int			fc_extra_cols = 0;

				/*
				 * CTE的RTE已经添加了搜索和循环列，
				 * 但子查询没有，所以跳过查找它们。
				 */
				if (fc_cte->search_clause)
					fc_extra_cols += 1;
				if (fc_cte->cycle_clause)
					fc_extra_cols += 2;
				if (fc_extra_cols &&
					fc_attnum > list_length(fc_tl) &&
					fc_attnum <= list_length(fc_tl) + fc_extra_cols)
					break;

				fc_ste = get_tle_by_resno(fc_tl, fc_attnum);
				if (fc_ste == NULL || fc_ste->resjunk)
					elog(ERROR, "CTE %s does not have attribute %d",
						 fc_rte->eref->aliasname, fc_attnum);
				fc_tle->resorigtbl = fc_ste->resorigtbl;
				fc_tle->resorigcol = fc_ste->resorigcol;
			}
			break;
	}
}


/*
 * transformAssignedExpr()
 *  仅在INSERT和UPDATE语句中使用。它准备将
 *  表达式分配给目标表的列。
 *  这包括将给定值强制转换为目标列的类型
 *  （如果必要），以及处理附加到目标列本身的
 *  任何子字段名称或下标。输入表达式已
 *  经过transformExpr()处理。
 *
 * pstate    解析状态
 * expr      要修改的表达式
 * exprKind  表示我们处理的语句类型
 * colname   目标列名称（即，将分配给的属性名称）
 * attrno    目标属性编号
 * indirection  目标列的下标/字段名称（如果有）
 * location  目标列的错误光标位置，或-1
 *
 * 返回修改后的表达式。
 *
 * 注意：location指向目标列名称（设置目标或插入
 * 列名称列表条目），因此在省略列名称列表的插入中它必须
 * 为-1。因此，我们通常应该更倾向于使用
 * exprLocation(expr)来处理可能在默认插入中发生的错误。
 */
Expr * transformAssignedExpr(ParseState *fc_pstate,
					  Expr *fc_expr,
					  ParseExprKind fc_exprKind,
					  const char *fc_colname,
					  int fc_attrno,
					  List *fc_indirection,
					  int fc_location)
{
	Relation	fc_rd = fc_pstate->p_target_relation;
	Oid			fc_type_id;		/* 提供的值的类型 */
	Oid			fc_attrtype;		/* 目标列的类型 */
	int32		fc_attrtypmod;
	Oid			fc_attrcollation;	/* 目标列的排序 */
	ParseExprKind fc_sv_expr_kind;

	/*
	 * 保存和恢复我们正在解析的表达式类型的身份。我们必须
	 * 在这里设置p_expr_kind，因为我们可以在不经过
	 * transformExpr()的情况下解析下标。
	 */
	Assert(fc_exprKind != EXPR_KIND_NONE);
	fc_sv_expr_kind = fc_pstate->p_expr_kind;
	fc_pstate->p_expr_kind = fc_exprKind;

	Assert(fc_rd != NULL);
	if (fc_attrno <= 0)
		ereport(ERROR,
				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
				 errmsg("cannot assign to system column \"%s\"",
						fc_colname),
				 parser_errposition(fc_pstate, fc_location)));
	fc_attrtype = attnumTypeId(fc_rd, fc_attrno);
	fc_attrtypmod = TupleDescAttr(fc_rd->rd_att, fc_attrno - 1)->atttypmod;
	fc_attrcollation = TupleDescAttr(fc_rd->rd_att, fc_attrno - 1)->attcollation;

	/*
	 * 如果表达式是DEFAULT占位符，将属性的
	 * 类型/typmod/排序插入其中，以便exprType等将报告
	 * 正确的内容。（我们预期最终替换的默认
	 * 表达式实际上将具有此类型和typmod。排序
	 * 可能并不重要，但我们还是正确设置它。）此外，
	 * 拒绝尝试使用DEFAULT更新子字段或数组元素，因为
	 * 列的部分不可能有默认值。
	 */
	if (fc_expr && IsA(fc_expr, SetToDefault))
	{
		SetToDefault *fc_def = (SetToDefault *) fc_expr;

		fc_def->typeId = fc_attrtype;
		fc_def->typeMod = fc_attrtypmod;
		fc_def->collation = fc_attrcollation;
		if (fc_indirection)
		{
			if (IsA(linitial(fc_indirection), A_Indices))
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("cannot set an array element to DEFAULT"),
						 parser_errposition(fc_pstate, fc_location)));
			else
				ereport(ERROR,
						(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
						 errmsg("cannot set a subfield to DEFAULT"),
						 parser_errposition(fc_pstate, fc_location)));
		}
	}

	/* 现在我们可以安全地使用exprType()。 */
	fc_type_id = exprType((Node *) fc_expr);

	/*
	 * 如果目标列上有间接引用，请准备一个数组或
	 * 子字段赋值表达式。这将生成一个新的列值，
	 * 源值已被插入其中，然后可以将其放入
	 * 由INSERT或UPDATE构建的新元组中。
	 */
	if (fc_indirection)
	{
		Node	   *fc_colVar;

		if (fc_pstate->p_is_insert)
		{
			/*
			 * 命令是INSERT INTO table (col.something) ... 所以没有
			 * 真实的源值来处理。插入一个NULL
			 * 常量作为源值。
			 */
			fc_colVar = (Node *) makeNullConst(fc_attrtype, fc_attrtypmod,
											fc_attrcollation);
		}
		else
		{
			/*
			 * 为要更新的列构建一个Var。
			 */
			Var		   *fc_var;

			fc_var = makeVar(fc_pstate->p_target_nsitem->p_rtindex, fc_attrno,
						  fc_attrtype, fc_attrtypmod, fc_attrcollation, 0);
			fc_var->location = fc_location;

			fc_colVar = (Node *) fc_var;
		}

		fc_expr = (Expr *)
			transformAssignmentIndirection(fc_pstate,
										   fc_colVar,
										   fc_colname,
										   false,
										   fc_attrtype,
										   fc_attrtypmod,
										   fc_attrcollation,
										   fc_indirection,
										   list_head(fc_indirection),
										   (Node *) fc_expr,
										   COERCION_ASSIGNMENT,
										   fc_location);
	}
	else
	{
		/*
		 * 对于普通的非合格目标列，进行类型检查和
		 * 强制转换。
		 */
		Node	   *fc_orig_expr = (Node *) fc_expr;

		fc_expr = (Expr *)
			coerce_to_target_type(fc_pstate,
								  fc_orig_expr, fc_type_id,
								  fc_attrtype, fc_attrtypmod,
								  COERCION_ASSIGNMENT,
								  COERCE_IMPLICIT_CAST,
								  -1);
		if (fc_expr == NULL)
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("column \"%s\" is of type %s"
							" but expression is of type %s",
							fc_colname,
							format_type_be(fc_attrtype),
							format_type_be(fc_type_id)),
					 errhint("You will need to rewrite or cast the expression."),
					 parser_errposition(fc_pstate, exprLocation(fc_orig_expr))));
	}

	fc_pstate->p_expr_kind = fc_sv_expr_kind;

	return fc_expr;
}


/*
 * updateTargetListEntry()
 *  仅在UPDATE语句（及ON CONFLICT DO UPDATE）中使用。
 *  它准备一个用于分配给目标表列的UPDATE TargetEntry。
 *  这包括将给定值强制转换为目标列的类型（如果必要），
 *  以及处理附加到目标列本身的任何子字段名称或下标。
 *
 * pstate      解析状态
 * tle         要修改的目标列表条目
 * colname     目标列名称（即，将分配给的属性名称）
 * attrno      目标属性编号
 * indirection  目标列的下标/字段名称（如果有）
 * location    错误光标位置（应指向列名称），或-1
 */
void updateTargetListEntry(ParseState *fc_pstate,
					  TargetEntry *fc_tle,
					  char *fc_colname,
					  int fc_attrno,
					  List *fc_indirection,
					  int fc_location)
{
	/* 根据需要修复表达式 */
	fc_tle->expr = transformAssignedExpr(fc_pstate,
									  fc_tle->expr,
									  EXPR_KIND_UPDATE_TARGET,
									  fc_colname,
									  fc_attrno,
									  fc_indirection,
									  fc_location);

	/*
	 * 设置 resno 以标识目标列——重写器和
	 * 规划者依赖于此。我们还设置 resname 以标识目标
	 * 列，但这仅用于调试目的；不应依赖于此。
	 * （特别是，它可能在存储的规则中已过时。）
	 */
	fc_tle->resno = (AttrNumber) fc_attrno;
	fc_tle->resname = fc_colname;
}


/*
 * 处理 INSERT/UPDATE/赋值中的目标列的间接引用（字段选择或下标）。
 * 此例程递归处理多个
 * 间接引用级别——但请注意，在间接引用列表中的多个相邻 A_Indices 节点
 * 被视为一个单一的多维下标操作。
 *
 * 在初始调用中，basenode 是 UPDATE 中目标列的 Var，
 * 或 INSERT 中目标类型的 null Const，或 PL/pgSQL 赋值中的目标变量的 Param。
 * 在递归调用中，basenode 为 NULL，
 * 表示如果需要，则应创建一个替代节点。
 *
 * targetName 是我们要赋值的字段或子字段的名称，
 * 如果我们在进行下标操作，则 targetIsSubscripting 为真。
 * 这些仅用于错误报告。
 *
 * targetTypeId、targetTypMod、targetCollation 表示要赋值对象的数据类型和
 * 排序规则（最初为目标列，
 * 稍后为某个子对象）。
 *
 * indirection 是间接引用节点的列表，indirection_cell 是剩余
 * 待处理的子列表的起始位置。当它为 NULL 时，我们完成
 * 递归，只需强制转换并返回 RHS。
 *
 * rhs 是要赋值的已转换值；请注意，未将其强制转换为特定类型。
 *
 * ccontext 是在强制转换 rhs 时使用的强制级别。
 * 对于正常语句，它将是 COERCION_ASSIGNMENT，但 PL/pgSQL 使用
 * 特殊值。
 *
 * location 是任何错误的光标错误位置。
 * （注意：这指向目标子句的开头，例如 "foo" 在 "foo.bar[baz]" 中。
 * 之后我们可能想要为间接引用单元装饰它们自己的位置
 * 信息，在这种情况下，这个 location 参数可以被省略。）
 */
Node * transformAssignmentIndirection(ParseState *fc_pstate,
							   Node *fc_basenode,
							   const char *fc_targetName,
							   bool fc_targetIsSubscripting,
							   Oid fc_targetTypeId,
							   int32 fc_targetTypMod,
							   Oid fc_targetCollation,
							   List *fc_indirection,
							   ListCell *fc_indirection_cell,
							   Node *fc_rhs,
							   CoercionContext fc_ccontext,
							   int fc_location)
{
	Node	   *fc_result;
	List	   *fc_subscripts = NIL;
	bool		fc_isSlice = false;
	ListCell   *fc_i;

	if (fc_indirection_cell && !fc_basenode)
	{
		/*
		 * 设置一个替代项。我们滥用 CaseTestExpr 来实现这个。
		 * 这样做是安全的，因为在完成的表达式中，唯一会
		 * 在 CaseTestExpr 之上的是 FieldStore 和 SubscriptingRef
		 * 节点。（树中可能还有其他内容，但都将
		 * 在这些节点类型的其他子字段内。）
		 */
		CaseTestExpr *fc_ctest = makeNode(CaseTestExpr);

		fc_ctest->typeId = fc_targetTypeId;
		fc_ctest->typeMod = fc_targetTypMod;
		fc_ctest->collation = fc_targetCollation;
		fc_basenode = (Node *) fc_ctest;
	}

	/*
	 * 我们必须将任何字段选择操作与下标化操作分开。相邻的 A_Indices 节点必须视为单个多维下标操作。
	 */
	for_each_cell(fc_i, fc_indirection, fc_indirection_cell)
	{
		Node	   *fc_n = lfirst(fc_i);

		if (IsA(fc_n, A_Indices))
		{
			fc_subscripts = lappend(fc_subscripts, fc_n);
			if (((A_Indices *) fc_n)->is_slice)
				fc_isSlice = true;
		}
		else if (IsA(fc_n, A_Star))
		{
			ereport(ERROR,
					(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
					 errmsg("row expansion via \"*\" is not supported here"),
					 parser_errposition(fc_pstate, fc_location)));
		}
		else
		{
			FieldStore *fc_fstore;
			Oid			fc_baseTypeId;
			int32		fc_baseTypeMod;
			Oid			fc_typrelid;
			AttrNumber	fc_attnum;
			Oid			fc_fieldTypeId;
			int32		fc_fieldTypMod;
			Oid			fc_fieldCollation;

			Assert(IsA(fc_n, String));

			/* 在此字段选择之前处理下标 */
			if (fc_subscripts)
			{
				/* 递归，然后返回，因为我们完成了 */
				return fc_transformAssignmentSubscripts(fc_pstate,
													 fc_basenode,
													 fc_targetName,
													 fc_targetTypeId,
													 fc_targetTypMod,
													 fc_targetCollation,
													 fc_subscripts,
													 fc_isSlice,
													 fc_indirection,
													 fc_i,
													 fc_rhs,
													 fc_ccontext,
													 fc_location);
			}

			/* 没有下标，因此可以在此处处理字段选择 */

			/*
			 * 查找复合类型，考虑到我们得到的可能是复合类型的域。
			 */
			fc_baseTypeMod = fc_targetTypMod;
			fc_baseTypeId = getBaseTypeAndTypmod(fc_targetTypeId, &fc_baseTypeMod);

			fc_typrelid = typeidTypeRelid(fc_baseTypeId);
			if (!fc_typrelid)
				ereport(ERROR,
						(errcode(ERRCODE_DATATYPE_MISMATCH),
						 errmsg("cannot assign to field \"%s\" of column \"%s\" because its type %s is not a composite type",
								strVal(fc_n), fc_targetName,
								format_type_be(fc_targetTypeId)),
						 parser_errposition(fc_pstate, fc_location)));

			fc_attnum = get_attnum(fc_typrelid, strVal(fc_n));
			if (fc_attnum == InvalidAttrNumber)
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_COLUMN),
						 errmsg("cannot assign to field \"%s\" of column \"%s\" because there is no such column in data type %s",
								strVal(fc_n), fc_targetName,
								format_type_be(fc_targetTypeId)),
						 parser_errposition(fc_pstate, fc_location)));
			if (fc_attnum < 0)
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_COLUMN),
						 errmsg("cannot assign to system column \"%s\"",
								strVal(fc_n)),
						 parser_errposition(fc_pstate, fc_location)));

			get_atttypetypmodcoll(fc_typrelid, fc_attnum,
								  &fc_fieldTypeId, &fc_fieldTypMod, &fc_fieldCollation);

			/* 递归以创建适当的 RHS 以进行字段赋值 */
			fc_rhs = transformAssignmentIndirection(fc_pstate,
												 NULL,
												 strVal(fc_n),
												 false,
												 fc_fieldTypeId,
												 fc_fieldTypMod,
												 fc_fieldCollation,
												 fc_indirection,
												 lnext(fc_indirection, fc_i),
												 fc_rhs,
												 fc_ccontext,
												 fc_location);

			/* 并构建一个 FieldStore 节点 */
			fc_fstore = makeNode(FieldStore);
			fc_fstore->arg = (Expr *) fc_basenode;
			fc_fstore->newvals = list_make1(fc_rhs);
			fc_fstore->fieldnums = list_make1_int(fc_attnum);
			fc_fstore->resulttype = fc_baseTypeId;

			
/*
			 * 如果目标是一个域，应用约束。 注意这并不完全正确：我们构建的表达式树会检查
			 * 域的约束在仅有此一个字段被填充或更新的复合值上，这可能导致不希望的失败。
			 * 重写器会将对同一表列的任何子字段赋值合并在一起，导致域的约束仅在我们为
			 * INSERT 或 UPDATE 所指的所有字段赋值后检查一次。
			 */
			if (fc_baseTypeId != fc_targetTypeId)
				return coerce_to_domain((Node *) fc_fstore,
										fc_baseTypeId, fc_baseTypeMod,
										fc_targetTypeId,
										COERCION_IMPLICIT,
										COERCE_IMPLICIT_CAST,
										fc_location,
										false);

			return (Node *) fc_fstore;
		}
	}

	/* 处理尾随下标（如果有） */
	if (fc_subscripts)
	{
		/* 递归，然后返回，因为我们完成了 */
		return fc_transformAssignmentSubscripts(fc_pstate,
											 fc_basenode,
											 fc_targetName,
											 fc_targetTypeId,
											 fc_targetTypMod,
											 fc_targetCollation,
											 fc_subscripts,
											 fc_isSlice,
											 fc_indirection,
											 NULL,
											 fc_rhs,
											 fc_ccontext,
											 fc_location);
	}

	/* 基本情况：只是将右侧值强制转换为匹配目标类型 ID */

	fc_result = coerce_to_target_type(fc_pstate,
								   fc_rhs, exprType(fc_rhs),
								   fc_targetTypeId, fc_targetTypMod,
								   fc_ccontext,
								   COERCE_IMPLICIT_CAST,
								   -1);
	if (fc_result == NULL)
	{
		if (fc_targetIsSubscripting)
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("subscripted assignment to \"%s\" requires type %s"
							" but expression is of type %s",
							fc_targetName,
							format_type_be(fc_targetTypeId),
							format_type_be(exprType(fc_rhs))),
					 errhint("You will need to rewrite or cast the expression."),
					 parser_errposition(fc_pstate, fc_location)));
		else
			ereport(ERROR,
					(errcode(ERRCODE_DATATYPE_MISMATCH),
					 errmsg("subfield \"%s\" is of type %s"
							" but expression is of type %s",
							fc_targetName,
							format_type_be(fc_targetTypeId),
							format_type_be(exprType(fc_rhs))),
					 errhint("You will need to rewrite or cast the expression."),
					 parser_errposition(fc_pstate, fc_location)));
	}

	return fc_result;
}

/*
 * transformAssignmentIndirection 的辅助手段：处理容器赋值
 */
static Node * fc_transformAssignmentSubscripts(ParseState *fc_pstate,
							  Node *fc_basenode,
							  const char *fc_targetName,
							  Oid fc_targetTypeId,
							  int32 fc_targetTypMod,
							  Oid fc_targetCollation,
							  List *fc_subscripts,
							  bool fc_isSlice,
							  List *fc_indirection,
							  ListCell *fc_next_indirection,
							  Node *fc_rhs,
							  CoercionContext fc_ccontext,
							  int fc_location)
{
	Node	   *fc_result;
	SubscriptingRef *fc_sbsref;
	Oid			fc_containerType;
	int32		fc_containerTypMod;
	Oid			fc_typeNeeded;
	int32		fc_typmodNeeded;
	Oid			fc_collationNeeded;

	Assert(fc_subscripts != NIL);

	/* 确定实际涉及的容器类型 */
	fc_containerType = fc_targetTypeId;
	fc_containerTypMod = fc_targetTypMod;
	transformContainerType(&fc_containerType, &fc_containerTypMod);

	/* 处理下标并识别右侧值所需的类型 */
	fc_sbsref = transformContainerSubscripts(fc_pstate,
										  fc_basenode,
										  fc_containerType,
										  fc_containerTypMod,
										  fc_subscripts,
										  true);

	fc_typeNeeded = fc_sbsref->refrestype;
	fc_typmodNeeded = fc_sbsref->reftypmod;

	/*
	 * 容器通常具有与其元素相同的排序规则，但有一个例外：我们可能正在对容器类型进行子脚本操作。在这种情况下使用基类型的排序规则。（对于任意子脚本语义，这种做法是不可靠的，但这没什么关系，因为我们仅仅用它来标记可能的 CaseTestExpr 的排序规则。）
	 */
	if (fc_containerType == fc_targetTypeId)
		fc_collationNeeded = fc_targetCollation;
	else
		fc_collationNeeded = get_typcollation(fc_containerType);

	/* 递归创建适当的 RHS 以便进行容器赋值 */
	fc_rhs = transformAssignmentIndirection(fc_pstate,
										 NULL,
										 fc_targetName,
										 true,
										 fc_typeNeeded,
										 fc_typmodNeeded,
										 fc_collationNeeded,
										 fc_indirection,
										 fc_next_indirection,
										 fc_rhs,
										 fc_ccontext,
										 fc_location);

	/*
	 * 将已经正确强制转换的 RHS 插入到 SubscriptingRef 中。然后将 refrestype 和 reftypmod 设置回容器类型的值。
	 */
	fc_sbsref->refassgnexpr = (Expr *) fc_rhs;
	fc_sbsref->refrestype = fc_containerType;
	fc_sbsref->reftypmod = fc_containerTypMod;

	fc_result = (Node *) fc_sbsref;

	/*
	 * 如果目标是一个容器上的域，需强制转换到该域。如同 transformAssignmentIndirection， 如果查询赋值到容器的多个元素，这种强制转换是过早的；但是我们将在查询重写过程中修复这一点。
	 */
	if (fc_containerType != fc_targetTypeId)
	{
		Oid			fc_resulttype = exprType(fc_result);

		fc_result = coerce_to_target_type(fc_pstate,
									   fc_result, fc_resulttype,
									   fc_targetTypeId, fc_targetTypMod,
									   fc_ccontext,
									   COERCE_IMPLICIT_CAST,
									   -1);
		/* 如果我们有 int2vector/oidvector，则可能会失败，但对真域则不会 */
		if (fc_result == NULL)
			ereport(ERROR,
					(errcode(ERRCODE_CANNOT_COERCE),
					 errmsg("cannot cast type %s to %s",
							format_type_be(fc_resulttype),
							format_type_be(fc_targetTypeId)),
					 parser_errposition(fc_pstate, fc_location)));
	}

	return fc_result;
}


/*
 * checkInsertTargets -
 *	  如果未提供，则生成 INSERT 列目标的列表，或者
 *	  测试提供的列名，以确保它们在目标表中。
 *	  还返回包含列属性编号的整型列表。
 */
List * checkInsertTargets(ParseState *fc_pstate, List *fc_cols, List **fc_attrnos)
{
	*fc_attrnos = NIL;

	if (fc_cols == NIL)
	{
		/*
		 * 生成 INSERT 的默认列列表。
		 */
		int			fc_numcol = RelationGetNumberOfAttributes(fc_pstate->p_target_relation);

		int			fc_i;

		for (fc_i = 0; fc_i < fc_numcol; fc_i++)
		{
			ResTarget  *fc_col;
			Form_pg_attribute fc_attr;

			fc_attr = TupleDescAttr(fc_pstate->p_target_relation->rd_att, fc_i);

			if (fc_attr->attisdropped)
				continue;

			fc_col = makeNode(ResTarget);
			fc_col->name = pstrdup(NameStr(fc_attr->attname));
			fc_col->indirection = NIL;
			fc_col->val = NULL;
			fc_col->location = -1;
			fc_cols = lappend(fc_cols, fc_col);
			*fc_attrnos = lappend_int(*fc_attrnos, fc_i + 1);
		}
	}
	else
	{
		/*
		 * 对用户提供的 INSERT 列表进行初始验证。
		 */
		Bitmapset  *fc_wholecols = NULL;
		Bitmapset  *fc_partialcols = NULL;
		ListCell   *fc_tl;

		foreach(fc_tl, fc_cols)
		{
			ResTarget  *fc_col = (ResTarget *) lfirst(fc_tl);
			char	   *fc_name = fc_col->name;
			int			fc_attrno;

			/* 查找列名，失败时报告错误 */
			fc_attrno = attnameAttNum(fc_pstate->p_target_relation, fc_name, false);
			if (fc_attrno == InvalidAttrNumber)
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_COLUMN),
						 errmsg("column \"%s\" of relation \"%s\" does not exist",
								fc_name,
								RelationGetRelationName(fc_pstate->p_target_relation)),
						 parser_errposition(fc_pstate, fc_col->location)));

			/*
			 * 检查重复，但仅限于整体列 --- 我们允许
			 * INSERT INTO foo (col.subcol1, col.subcol2)
			 */
			if (fc_col->indirection == NIL)
			{
				/* 整列；不得有其他赋值 */
				if (bms_is_member(fc_attrno, fc_wholecols) ||
					bms_is_member(fc_attrno, fc_partialcols))
					ereport(ERROR,
							(errcode(ERRCODE_DUPLICATE_COLUMN),
							 errmsg("column \"%s\" specified more than once",
									fc_name),
							 parser_errposition(fc_pstate, fc_col->location)));
				fc_wholecols = bms_add_member(fc_wholecols, fc_attrno);
			}
			else
			{
				/* 部分列；不得有任何完整赋值 */
				if (bms_is_member(fc_attrno, fc_wholecols))
					ereport(ERROR,
							(errcode(ERRCODE_DUPLICATE_COLUMN),
							 errmsg("column \"%s\" specified more than once",
									fc_name),
							 parser_errposition(fc_pstate, fc_col->location)));
				fc_partialcols = bms_add_member(fc_partialcols, fc_attrno);
			}

			*fc_attrnos = lappend_int(*fc_attrnos, fc_attrno);
		}
	}

	return fc_cols;
}

/*
 * ExpandColumnRefStar()
 *		将 foo.* 转换为表达式或目标列表条目的列表。
 *
 * 这处理 '*' 作为 ColumnRef 中最后一个或唯一项目的情况。该代码在 SELECT 目标列表的 foo.* 顶层（我们希望结果中有 TargetEntry 节点）和 ROW() 或 VALUES() 结构中的 foo.*（我们仅希望有基本表达式）之间共享。
 *
 * 被引用的列标记为需要 SELECT 访问权限。
 */
static List * fc_ExpandColumnRefStar(ParseState *fc_pstate, ColumnRef *fc_cref,
					bool fc_make_target_entry)
{
	List	   *fc_fields = fc_cref->fields;
	int			fc_numnames = list_length(fc_fields);

	if (fc_numnames == 1)
	{
		/*
		 * 目标项是一个裸 '*'，展开所有表
		 *
		 * (例如，SELECT * FROM emp, dept)
		 *
		 * 由于语法仅接受顶层 SELECT 中的裸 '*'，因此我们在这里不需要处理 make_target_entry==false 的情况。
		 */
		Assert(fc_make_target_entry);
		return fc_ExpandAllTables(fc_pstate, fc_cref->location);
	}
	else
	{
		/*
		 * 目标项是 relation.*，展开该表
		 *
		 * (例如，SELECT emp.*, dname FROM emp, dept)
		 *
		 * 注意：这段代码与 transformColumnRef 十分相似；我们很想调用它，然后用变量列表替换结果中的整个行 Var。然而，这样会导致 RTE 的 selectedCols 位图显示整个行需要选择权限，以及各个列。这是不正确的（因为后添加的列不应需要选择权限）。我们可以尝试在事后删除整个行权限位，但重复代码更简单。
		 */
		char	   *fc_nspname = NULL;
		char	   *fc_relname = NULL;
		ParseNamespaceItem *fc_nsitem = NULL;
		int			fc_levels_up;
		enum
		{
			CRSERR_NO_RTE,
			CRSERR_WRONG_DB,
			CRSERR_TOO_MANY
		}			fc_crserr = CRSERR_NO_RTE;

		/*
		 * 如果有 PreParseColumnRefHook，首先尝试一下。如果它返回非空值，则我们应该使用该表达式。
		 */
		if (fc_pstate->p_pre_columnref_hook != NULL)
		{
			Node	   *fc_node;

			fc_node = fc_pstate->p_pre_columnref_hook(fc_pstate, fc_cref);
			if (fc_node != NULL)
				return fc_ExpandRowReference(fc_pstate, fc_node, fc_make_target_entry);
		}

		switch (fc_numnames)
		{
			case 2:
				fc_relname = strVal(linitial(fc_fields));
				fc_nsitem = refnameNamespaceItem(fc_pstate, fc_nspname, fc_relname,
											  fc_cref->location,
											  &fc_levels_up);
				break;
			case 3:
				fc_nspname = strVal(linitial(fc_fields));
				fc_relname = strVal(lsecond(fc_fields));
				fc_nsitem = refnameNamespaceItem(fc_pstate, fc_nspname, fc_relname,
											  fc_cref->location,
											  &fc_levels_up);
				break;
			case 4:
				{
					char	   *fc_catname = strVal(linitial(fc_fields));

					/*
					 * 我们检查目录名称，然后忽略它。
					 */
					if (strcmp(fc_catname, get_database_name(MyDatabaseId)) != 0)
					{
						fc_crserr = CRSERR_WRONG_DB;
						break;
					}
					fc_nspname = strVal(lsecond(fc_fields));
					fc_relname = strVal(lthird(fc_fields));
					fc_nsitem = refnameNamespaceItem(fc_pstate, fc_nspname, fc_relname,
												  fc_cref->location,
												  &fc_levels_up);
					break;
				}
			default:
				fc_crserr = CRSERR_TOO_MANY;
				break;
		}

		/*
		 * 现在给 PostParseColumnRefHook 一个机会。如果有的话，我们通过传递 RangeTblEntry 而不是 Var 作为计划翻译来作弊。（单个 Var 无论如何都不严格正确。这种约定允许真正关心的钩子知道发生了什么。传递 nsitem 可能更好，但我们需要将该结构提升为完整的 Node 类型，以便被调用者可以识别其类型。）
		 */
		if (fc_pstate->p_post_columnref_hook != NULL)
		{
			Node	   *fc_node;

			fc_node = fc_pstate->p_post_columnref_hook(fc_pstate, fc_cref,
												 (Node *) (fc_nsitem ? fc_nsitem->p_rte : NULL));
			if (fc_node != NULL)
			{
				if (fc_nsitem != NULL)
					ereport(ERROR,
							(errcode(ERRCODE_AMBIGUOUS_COLUMN),
							 errmsg("column reference \"%s\" is ambiguous",
									NameListToString(fc_cref->fields)),
							 parser_errposition(fc_pstate, fc_cref->location)));
				return fc_ExpandRowReference(fc_pstate, fc_node, fc_make_target_entry);
			}
		}

		/*
		 * 如果未找到翻译，则抛出错误。
		 */
		if (fc_nsitem == NULL)
		{
			switch (fc_crserr)
			{
				case CRSERR_NO_RTE:
					errorMissingRTE(fc_pstate, makeRangeVar(fc_nspname, fc_relname,
														 fc_cref->location));
					break;
				case CRSERR_WRONG_DB:
					ereport(ERROR,
							(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
							 errmsg("cross-database references are not implemented: %s",
									NameListToString(fc_cref->fields)),
							 parser_errposition(fc_pstate, fc_cref->location)));
					break;
				case CRSERR_TOO_MANY:
					ereport(ERROR,
							(errcode(ERRCODE_SYNTAX_ERROR),
							 errmsg("improper qualified name (too many dotted names): %s",
									NameListToString(fc_cref->fields)),
							 parser_errposition(fc_pstate, fc_cref->location)));
					break;
			}
		}

		/*
		 * 好的，将 nsitem 展开为字段。
		 */
		return fc_ExpandSingleTable(fc_pstate, fc_nsitem, fc_levels_up, fc_cref->location,
								 fc_make_target_entry);
	}
}

/*
 * ExpandAllTables()
 *		将目标列表中的 '*' 转换为目标列表条目的列表。
 *
 * 对于每个可见于未限定列名访问的关系，生成 tlist 条目。我们不考虑仅限限定名称的条目，因为这将包括没有别名的 JOIN 的输入表、NEW/OLD 伪条目等。
 *
 * 被引用的关系/列标记为需要 SELECT 访问权限。
 */
static List * fc_ExpandAllTables(ParseState *fc_pstate, int fc_location)
{
	List	   *fc_target = NIL;
	bool		fc_found_table = false;
	ListCell   *fc_l;

	foreach(fc_l, fc_pstate->p_namespace)
	{
		ParseNamespaceItem *fc_nsitem = (ParseNamespaceItem *) lfirst(fc_l);

		/* 忽略仅表项 */
		if (!fc_nsitem->p_cols_visible)
			continue;
		/* 解析目标列表时不应有任何仅限 lateral 的项目 */
		Assert(!fc_nsitem->p_lateral_only);
		/* 记住我们找到一个 p_cols_visible 项 */
		fc_found_table = true;

		fc_target = list_concat(fc_target,
							 expandNSItemAttrs(fc_pstate,
											   fc_nsitem,
											   0,
											   true,
											   fc_location));
	}

	/*
	 * 检查“SELECT *;”。我们这样做，而不是检查
	 * 目标 == NIL，因为我们想允许从一个零列
	 * 表中 SELECT *。
	 */
	if (!fc_found_table)
		ereport(ERROR,
				(errcode(ERRCODE_SYNTAX_ERROR),
				 errmsg("SELECT * with no tables specified is not valid"),
				 parser_errposition(fc_pstate, fc_location)));

	return fc_target;
}

/*
 * ExpandIndirectionStar()
 *		将 foo.* 转换为表达式或目标列表项的列表。
 *
 * 这处理了 '*' 作为 A_Indirection 中最后一个项目出现的情况。
 * 该代码在 SELECT 目标列表的顶层中 foo.* 以及在 ROW() 或 VALUES() 构造中的
 * foo.* 之间共享（在此我们只想要原始表达式）。
 * 为了稳健性，我们使用一个单独的 "make_target_entry" 标志来控制
 * 这一点，而不是依赖于 exprKind。
 */
static List * fc_ExpandIndirectionStar(ParseState *fc_pstate, A_Indirection *fc_ind,
					  bool fc_make_target_entry, ParseExprKind fc_exprKind)
{
	Node	   *fc_expr;

	/* 去掉 '*' 以创建对行类型对象的引用 */
	fc_ind = copyObject(fc_ind);
	fc_ind->indirection = list_truncate(fc_ind->indirection,
									 list_length(fc_ind->indirection) - 1);

	/* 并转换它 */
	fc_expr = transformExpr(fc_pstate, (Node *) fc_ind, fc_exprKind);

	/* 将行类型表达式展开为单独的字段 */
	return fc_ExpandRowReference(fc_pstate, fc_expr, fc_make_target_entry);
}

/*
 * ExpandSingleTable()
 *		将 foo.* 转换为表达式或目标列表条目的列表。
 *
 * 这处理了 foo 已被确定为对 RTE 的简单引用的情况，
 * 因此我们可以直接为表达式生成 Vars。
 *
 * 相关列被标记为需要 SELECT 访问。
 */
static List * fc_ExpandSingleTable(ParseState *fc_pstate, ParseNamespaceItem *fc_nsitem,
				  int fc_sublevels_up, int fc_location, bool fc_make_target_entry)
{
	if (fc_make_target_entry)
	{
		/* expandNSItemAttrs 处理权限标记 */
		return expandNSItemAttrs(fc_pstate, fc_nsitem, fc_sublevels_up, true, fc_location);
	}
	else
	{
		RangeTblEntry *fc_rte = fc_nsitem->p_rte;
		List	   *fc_vars;
		ListCell   *fc_l;

		fc_vars = expandNSItemVars(fc_nsitem, fc_sublevels_up, fc_location, NULL);

		/*
		 * 需要对表进行读取访问。这通常与下面的markVarForSelectPriv调用冗余，但如果表没有列，则不是。如果nsitem是用于连接的，则不需要做任何事情：其组成表将在添加到rangetable时标记为ACL_SELECT。（此步骤仅改变UPDATE/DELETE的目标关系，目标关系不能在连接下。）
		 */
		if (fc_rte->rtekind == RTE_RELATION)
			fc_rte->requiredPerms |= ACL_SELECT;

		/* 需要每一列的读取访问权限 */
		foreach(fc_l, fc_vars)
		{
			Var		   *fc_var = (Var *) lfirst(fc_l);

			markVarForSelectPriv(fc_pstate, fc_var);
		}

		return fc_vars;
	}
}

/*
 * ExpandRowReference()
 *		将foo.*转换为表达式或目标列表条目的列表。
 *
 * 这处理foo是复合类型任意表达式的情况。
 */
static List * fc_ExpandRowReference(ParseState *fc_pstate, Node *fc_expr,
				   bool fc_make_target_entry)
{
	List	   *fc_result = NIL;
	TupleDesc	fc_tupleDesc;
	int			fc_numAttrs;
	int			fc_i;

	/*
	 * 如果rowtype表达式是整个行Var，我们可以将字段扩展为简单Vars。注意：如果RTE是一个关系，此情况会留下RTE的selectedCols位图，显示整个行需要选择权限，以及单个列。然而，我们只能在像(table.*).*这种奇怪的表示法中到达这里，所以不值得尝试清理---可以说，这种情况下的权限标记是正确的。
	 */
	if (IsA(fc_expr, Var) &&
		((Var *) fc_expr)->varattno == InvalidAttrNumber)
	{
		Var		   *fc_var = (Var *) fc_expr;
		ParseNamespaceItem *fc_nsitem;

		fc_nsitem = GetNSItemByRangeTablePosn(fc_pstate, fc_var->varno, fc_var->varlevelsup);
		return fc_ExpandSingleTable(fc_pstate, fc_nsitem, fc_var->varlevelsup, fc_var->location, fc_make_target_entry);
	}

	/*
	 * 否则，我们必须以艰难的方式进行。我们当前的实现是生成多个表达式副本并进行FieldSelects。（如果表达式涉及非平凡的计算，这可能会非常低效 :-(。）
	 *
	 * 验证它是复合类型，并获取tupdesc。
	 * get_expr_result_tupdesc()方便地处理此问题。
	 *
	 * 如果它是类型为RECORD的Var，我们必须更加努力：我们必须找出Var引用的内容，并将其传递给get_expr_result_tupdesc。这个任务由expandRecordVariable()处理。
	 */
	if (IsA(fc_expr, Var) &&
		((Var *) fc_expr)->vartype == RECORDOID)
		fc_tupleDesc = expandRecordVariable(fc_pstate, (Var *) fc_expr, 0);
	else
		fc_tupleDesc = get_expr_result_tupdesc(fc_expr, false);
	Assert(fc_tupleDesc);

	/* 生成对各个字段的引用列表 */
	fc_numAttrs = fc_tupleDesc->natts;
	for (fc_i = 0; fc_i < fc_numAttrs; fc_i++)
	{
		Form_pg_attribute fc_att = TupleDescAttr(fc_tupleDesc, fc_i);
		FieldSelect *fc_fselect;

		if (fc_att->attisdropped)
			continue;

		fc_fselect = makeNode(FieldSelect);
		fc_fselect->arg = (Expr *) copyObject(fc_expr);
		fc_fselect->fieldnum = fc_i + 1;
		fc_fselect->resulttype = fc_att->atttypid;
		fc_fselect->resulttypmod = fc_att->atttypmod;
		/* 保存属性的排序规则以供 parse_collate.c 使用 */
		fc_fselect->resultcollid = fc_att->attcollation;

		if (fc_make_target_entry)
		{
			/* 添加TargetEntry装饰 */
			TargetEntry *fc_te;

			fc_te = makeTargetEntry((Expr *) fc_fselect,
								 (AttrNumber) fc_pstate->p_next_resno++,
								 pstrdup(NameStr(fc_att->attname)),
								 false);
			fc_result = lappend(fc_result, fc_te);
		}
		else
			fc_result = lappend(fc_result, fc_fselect);
	}

	return fc_result;
}

/*
 * expandRecordVariable
 *		获取RECORD类型Var的元组描述，如果可能的话。
 *
 * 由于没有实际的表或视图列可以具有RECORD类型，因此这样的Var必须指向JOIN或FUNCTION RTE或子查询输出。我们深入查找以找到最终定义的表达式，并尝试从中推断tupdesc。如果我们无法确定tupdesc，我们会报告错误。
 *
 * levelsup是一个额外的偏移量，用于在递归时正确解释Var的varlevelsup。外部调用者应传递零。
 */
TupleDesc expandRecordVariable(ParseState *fc_pstate, Var *fc_var, int fc_levelsup)
{
	TupleDesc	fc_tupleDesc;
	int			fc_netlevelsup;
	RangeTblEntry *fc_rte;
	AttrNumber	fc_attnum;
	Node	   *fc_expr;

	/* 检查我的调用者没有搞错 */
	Assert(IsA(fc_var, Var));
	Assert(fc_var->vartype == RECORDOID);

	/*
	 * 注意：在这里使用GetNSItemByRangeTablePosn是很诱人的，这样我们可以使用expandNSItemVars而不是expandRTE；但这对于一些递归案例不起作用，在这些案例中，我们没有p_namespace数据而构建了一个ParseState。
	 */
	fc_netlevelsup = fc_var->varlevelsup + fc_levelsup;
	fc_rte = GetRTEByRangeTablePosn(fc_pstate, fc_var->varno, fc_netlevelsup);
	fc_attnum = fc_var->varattno;

	if (fc_attnum == InvalidAttrNumber)
	{
		/* 对RTE的整体行引用，因此扩展已知字段 */
		List	   *fc_names,
				   *fc_vars;
		ListCell   *fc_lname,
				   *fc_lvar;
		int			fc_i;

		expandRTE(fc_rte, fc_var->varno, 0, fc_var->location, false,
				  &fc_names, &fc_vars);

		fc_tupleDesc = CreateTemplateTupleDesc(list_length(fc_vars));
		fc_i = 1;
		forboth(fc_lname, fc_names, fc_lvar, fc_vars)
		{
			char	   *fc_label = strVal(lfirst(fc_lname));
			Node	   *fc_varnode = (Node *) lfirst(fc_lvar);

			TupleDescInitEntry(fc_tupleDesc, fc_i,
							   fc_label,
							   exprType(fc_varnode),
							   exprTypmod(fc_varnode),
							   0);
			TupleDescInitEntryCollation(fc_tupleDesc, fc_i,
										exprCollation(fc_varnode));
			fc_i++;
		}
		Assert(fc_lname == NULL && fc_lvar == NULL);	/* 列表长度相同？ */

		return fc_tupleDesc;
	}

	fc_expr = (Node *) fc_var;		/* 如果无法深入，则默认值 */

	switch (fc_rte->rtekind)
	{
		case RTE_RELATION:
		case RTE_VALUES:
		case RTE_NAMEDTUPLESTORE:
		case RTE_RESULT:

			/*
			 * 这种情况不应该发生：表、值列表或ENR的列不应具有RECORD类型。穿越并在底部失败（最有可能）。
			 */
			break;
		case RTE_SUBQUERY:
			{
				/* 子查询中的 FROM：检查子选择的输出表达式 */
				TargetEntry *fc_ste = get_tle_by_resno(fc_rte->subquery->targetList,
													fc_attnum);

				if (fc_ste == NULL || fc_ste->resjunk)
					elog(ERROR, "subquery %s does not have attribute %d",
						 fc_rte->eref->aliasname, fc_attnum);
				fc_expr = (Node *) fc_ste->expr;
				if (IsA(fc_expr, Var))
				{
					/*
					 * 递归到子选择中以查看其Var引用的内容。我们必须构建一个额外的ParseState层，以便与子选择中的varlevelsup保持同步；此外，子查询RTE可能来自外部查询层，在这种情况下，子选择的ParseState必须以该外部层为父。
					 */
					ParseState	fc_mypstate = {0};
					Index		fc_levelsup;

					/* 此循环必须有效，因为GetRTEByRangeTablePosn也有效 */
					for (fc_levelsup = 0; fc_levelsup < fc_netlevelsup; fc_levelsup++)
						fc_pstate = fc_pstate->parentParseState;
					fc_mypstate.parentParseState = fc_pstate;
					fc_mypstate.p_rtable = fc_rte->subquery->rtable;
					/* 不要费心填充假pstate的其余部分 */

					return expandRecordVariable(&fc_mypstate, (Var *) fc_expr, 0);
				}
				/* 否则穿越检查表达式 */
			}
			break;
		case RTE_JOIN:
			/* 连接RTE --- 递归检查别名变量 */
			Assert(fc_attnum > 0 && fc_attnum <= list_length(fc_rte->joinaliasvars));
			fc_expr = (Node *) list_nth(fc_rte->joinaliasvars, fc_attnum - 1);
			Assert(fc_expr != NULL);
			/* 我们故意不在这里剥离隐式强制转换 */
			if (IsA(fc_expr, Var))
				return expandRecordVariable(fc_pstate, (Var *) fc_expr, fc_netlevelsup);
			/* 否则穿越检查表达式 */
			break;
		case RTE_FUNCTION:

			/*
			 * 我们无法到达这里，除非一个函数声明了其中一个结果列为RECORD，这是不允许的。
			 */
			break;
		case RTE_TABLEFUNC:

			/*
			 * 表函数不能有RECORD类型的列。
			 */
			break;
		case RTE_CTE:
			/* CTE引用：检查子查询的输出表达式 */
			if (!fc_rte->self_reference)
			{
				CommonTableExpr *fc_cte = GetCTEForRTE(fc_pstate, fc_rte, fc_netlevelsup);
				TargetEntry *fc_ste;

				fc_ste = get_tle_by_resno(GetCTETargetList(fc_cte), fc_attnum);
				if (fc_ste == NULL || fc_ste->resjunk)
					elog(ERROR, "CTE %s does not have attribute %d",
						 fc_rte->eref->aliasname, fc_attnum);
				fc_expr = (Node *) fc_ste->expr;
				if (IsA(fc_expr, Var))
				{
					/*
					 * 递归到CTE以查看其Var引用的内容。我们必须构建一个额外的ParseState层，以便与CTE中的varlevelsup保持同步；此外，它可能是一个外部CTE（与上面的SUBQUERY案例比较）。
					 */
					ParseState	fc_mypstate = {0};
					Index		fc_levelsup;

					/* 此循环必须有效，因为GetCTEForRTE也有效 */
					for (fc_levelsup = 0;
						 fc_levelsup < fc_rte->ctelevelsup + fc_netlevelsup;
						 fc_levelsup++)
						fc_pstate = fc_pstate->parentParseState;
					fc_mypstate.parentParseState = fc_pstate;
					fc_mypstate.p_rtable = ((Query *) fc_cte->ctequery)->rtable;
					/* 不要费心填充假pstate的其余部分 */

					return expandRecordVariable(&fc_mypstate, (Var *) fc_expr, 0);
				}
				/* 否则穿越检查表达式 */
			}
			break;
	}

	/*
	 * 我们现在有一个无法扩展的表达式，所以看看get_expr_result_tupdesc()是否能对其做些什么。
	 */
	return get_expr_result_tupdesc(fc_expr, false);
}


/*
 * FigureColname -
 *	  如果目标列表中未指定结果列的名称，则必须猜测一个合适的名称。 SQL规范提供了一些指导，但不多...
 *
 * 请注意，参数是目标项的*未转换*解析树。这比转换过的树稍微容易处理一些。
 */
char * FigureColname(Node *fc_node)
{
	char	   *fc_name = NULL;

	(void) fc_FigureColnameInternal(fc_node, &fc_name);
	if (fc_name != NULL)
		return fc_name;
	/* 如果无法猜测任何内容，则采用默认结果 */
	return "?column?";
}


/*
 * FigureIndexColname -
 *	  选择索引中表达式列的名称
 *
 * 这实际上与 FigureColname 完全相同，除了当我们无法选择一个好的名称时返回 NULL。
 */
char * FigureIndexColname(Node *fc_node)
{
	char	   *fc_name = NULL;

	(void) fc_FigureColnameInternal(fc_node, &fc_name);
	return fc_name;
}

/*
 * FigureColnameInternal -
 *	  FigureColname 的内部工作马
 *
 * 返回值表示结果的信心程度：
 *		0 - 无信息
 *		1 - 第二好的名称选择
 *		2 - 好的名称选择
 * 返回值实际上只在内部使用。
 * 如果结果不是零，*name 被设置为所选名称。
 */
static int fc_FigureColnameInternal(Node *fc_node, char **fc_name)
{
	int			fc_strength = 0;

	if (fc_node == NULL)
		return fc_strength;

	switch (nodeTag(fc_node))
	{
		case T_ColumnRef:
			{
				char	   *fc_fname = NULL;
				ListCell   *fc_l;

				/* 查找最后一个字段名称（如果有），忽略 "*" */
				foreach(fc_l, ((ColumnRef *) fc_node)->fields)
				{
					Node	   *fc_i = lfirst(fc_l);

					if (IsA(fc_i, String))
						fc_fname = strVal(fc_i);
				}
				if (fc_fname)
				{
					*fc_name = fc_fname;
					return 2;
				}
			}
			break;
		case T_A_Indirection:
			{
				A_Indirection *fc_ind = (A_Indirection *) fc_node;
				char	   *fc_fname = NULL;
				ListCell   *fc_l;

				/* 找到最后一个字段名称（如果有），忽略 "*" 和下标 */
				foreach(fc_l, fc_ind->indirection)
				{
					Node	   *fc_i = lfirst(fc_l);

					if (IsA(fc_i, String))
						fc_fname = strVal(fc_i);
				}
				if (fc_fname)
				{
					*fc_name = fc_fname;
					return 2;
				}
				return fc_FigureColnameInternal(fc_ind->arg, fc_name);
			}
			break;
		case T_FuncCall:
			*fc_name = strVal(llast(((FuncCall *) fc_node)->funcname));
			return 2;
		case T_A_Expr:
			if (((A_Expr *) fc_node)->kind == AEXPR_NULLIF)
			{
				/* 使 nullif() 像一个常规函数一样工作 */
				*fc_name = "nullif";
				return 2;
			}
			break;
		case T_TypeCast:
			fc_strength = fc_FigureColnameInternal(((TypeCast *) fc_node)->arg,
											 fc_name);
			if (fc_strength <= 1)
			{
				if (((TypeCast *) fc_node)->typeName != NULL)
				{
					*fc_name = strVal(llast(((TypeCast *) fc_node)->typeName->names));
					return 1;
				}
			}
			break;
		case T_CollateClause:
			return fc_FigureColnameInternal(((CollateClause *) fc_node)->arg, fc_name);
		case T_GroupingFunc:
			/* 使 GROUPING() 像一个常规函数一样工作 */
			*fc_name = "grouping";
			return 2;
		case T_SubLink:
			switch (((SubLink *) fc_node)->subLinkType)
			{
				case EXISTS_SUBLINK:
					*fc_name = "exists";
					return 2;
				case ARRAY_SUBLINK:
					*fc_name = "array";
					return 2;
				case EXPR_SUBLINK:
					{
						/* 获取子查询的单个目标的列名 */
						SubLink    *fc_sublink = (SubLink *) fc_node;
						Query	   *fc_query = (Query *) fc_sublink->subselect;

						/* 
						 * 子查询可能已经被转换过，
						 * 但我们还是要小心检查一下。 （我们在这里能看到一个转换过的子查询的原因
						 * 是 transformSubLink 是懒惰的，并且在就地修改了 SubLink
						 * 节点。）
						 */
						if (IsA(fc_query, Query))
						{
							TargetEntry *fc_te = (TargetEntry *) linitial(fc_query->targetList);

							if (fc_te->resname)
							{
								*fc_name = fc_te->resname;
								return 2;
							}
						}
					}
					break;
					/* 与其他类似操作符的节点一样，这些没有名字 */
				case MULTIEXPR_SUBLINK:
				case ALL_SUBLINK:
				case ANY_SUBLINK:
				case ROWCOMPARE_SUBLINK:
				case CTE_SUBLINK:
					break;
			}
			break;
		case T_CaseExpr:
			fc_strength = fc_FigureColnameInternal((Node *) ((CaseExpr *) fc_node)->defresult,
											 fc_name);
			if (fc_strength <= 1)
			{
				*fc_name = "case";
				return 1;
			}
			break;
		case T_A_ArrayExpr:
			/* 使 ARRAY[] 像一个函数一样工作 */
			*fc_name = "array";
			return 2;
		case T_RowExpr:
			/* 使 ROW() 像一个函数一样工作 */
			*fc_name = "row";
			return 2;
		case T_CoalesceExpr:
			/* 使 coalesce() 像一个常规函数一样工作 */
			*fc_name = "coalesce";
			return 2;
		case T_MinMaxExpr:
			/* 使 greatest/least 像一个常规函数一样工作 */
			switch (((MinMaxExpr *) fc_node)->op)
			{
				case IS_GREATEST:
					*fc_name = "greatest";
					return 2;
				case IS_LEAST:
					*fc_name = "least";
					return 2;
			}
			break;
		case T_SQLValueFunction:
			/* 使这些像一个函数或变量一样工作 */
			switch (((SQLValueFunction *) fc_node)->op)
			{
				case SVFOP_CURRENT_DATE:
					*fc_name = "current_date";
					return 2;
				case SVFOP_CURRENT_TIME:
				case SVFOP_CURRENT_TIME_N:
					*fc_name = "current_time";
					return 2;
				case SVFOP_CURRENT_TIMESTAMP:
				case SVFOP_CURRENT_TIMESTAMP_N:
					*fc_name = "current_timestamp";
					return 2;
				case SVFOP_LOCALTIME:
				case SVFOP_LOCALTIME_N:
					*fc_name = "localtime";
					return 2;
				case SVFOP_LOCALTIMESTAMP:
				case SVFOP_LOCALTIMESTAMP_N:
					*fc_name = "localtimestamp";
					return 2;
				case SVFOP_CURRENT_ROLE:
					*fc_name = "current_role";
					return 2;
				case SVFOP_CURRENT_USER:
					*fc_name = "current_user";
					return 2;
				case SVFOP_USER:
					*fc_name = "user";
					return 2;
				case SVFOP_SESSION_USER:
					*fc_name = "session_user";
					return 2;
				case SVFOP_CURRENT_CATALOG:
					*fc_name = "current_catalog";
					return 2;
				case SVFOP_CURRENT_SCHEMA:
					*fc_name = "current_schema";
					return 2;
			}
			break;
		case T_XmlExpr:
			/* 使 SQL/XML 函数像一个常规函数一样工作 */
			switch (((XmlExpr *) fc_node)->op)
			{
				case IS_XMLCONCAT:
					*fc_name = "xmlconcat";
					return 2;
				case IS_XMLELEMENT:
					*fc_name = "xmlelement";
					return 2;
				case IS_XMLFOREST:
					*fc_name = "xmlforest";
					return 2;
				case IS_XMLPARSE:
					*fc_name = "xmlparse";
					return 2;
				case IS_XMLPI:
					*fc_name = "xmlpi";
					return 2;
				case IS_XMLROOT:
					*fc_name = "xmlroot";
					return 2;
				case IS_XMLSERIALIZE:
					*fc_name = "xmlserialize";
					return 2;
				case IS_DOCUMENT:
					/* 什么都没有 */
					break;
			}
			break;
		case T_XmlSerialize:
			*fc_name = "xmlserialize";
			return 2;
		default:
			break;
	}

	return fc_strength;
}
